package triangleWorm;

import java.util.LinkedList;

import processing.core.PApplet;
import processing.core.PConstants;
import processing.core.PVector;

@SuppressWarnings("unchecked")
public class IslandWorld implements IWorld{	
	PApplet p;
	
	//World size
	float worldWidth = 4000;
	float worldHeight = 2000;
	float worldDepth = worldWidth;
	PVector worldCenter = new PVector(WormApplication.WIDTH / 2f, WormApplication.HEIGHT / 2f, 0);
	
	int backgroundColor = 25;
	int seaColor = 15;

	//Island
	int meshVertexNum = 60;
	PVector[][] islandMesh = new PVector[meshVertexNum][meshVertexNum];
	float[][] islandMeshColors = new float[meshVertexNum][meshVertexNum];
	float islandMaxHeight = worldHeight / 3f;
	float islandNoiseScale = 0.2f;
	
	//Sky
	int starCount = 750;
	PVector[] stars = new PVector[starCount];
	LinkedList<Integer>[] starConstellations = (LinkedList<Integer>[]) new LinkedList[starCount];
	float constallationDist = 750;
	float[] starColors = new float[starCount];
	float starMaxColor = 255;
	float starMinColor = 50;
	
	//Sea
	int seaCircleNum = 200;
	PVector[] innerSeaCircle = new PVector[seaCircleNum];
	float innerCircleRadius = worldWidth * 0.4f;
	float innerCircleColor = seaColor;
	PVector[] outerSeaCircle = new PVector[seaCircleNum];
	float outerCircleRadius = worldWidth * 0.9f;
	float outerCircleColor = backgroundColor;
	
	
	
	public void init(PApplet processing) {
		p = processing;
		
		float pi = PConstants.PI;
		float halfPi = pi / 2f;
		float doublePi = PConstants.PI * 2f;
		
		//Initiate sky
		for(int i = 0; i < starCount; i ++) {
			stars[i] = new PVector();
			
			float xMin = WormApplication.WIDTH / 2 - worldWidth * 2;
			float xMax = WormApplication.WIDTH / 2 + worldWidth * 2;
			float xPos = p.random(xMin, xMax);
			
			float zMin = - worldDepth * 2;
			float zMax = worldDepth * 2;
			float zPos = p.random(zMin, zMax);
			
			float yMin = WormApplication.HEIGHT / 2 - worldHeight * 3;
			float yMax = WormApplication.HEIGHT / 2 + worldHeight;
			float xRatio = (xPos - xMin) / (xMax - xMin);
			float xFactor = PApplet.sin(xRatio * pi);
			float zRatio = (zPos - zMin) / (zMax - zMin);
			float zFactor = PApplet.sin(zRatio * pi);
			float yPos = yMax - (yMax - yMin) * xFactor * zFactor;
			
			stars[i].set(xPos, yPos, zPos);
			starColors[i] = starMinColor + (starMaxColor - starMinColor) * zFactor * xFactor;
		}		
		for(int i = 0; i < starCount; i ++) {
			starConstellations[i] = new LinkedList<Integer>();
			for(int j = i + 1; j < starCount; j ++) {
				if(constallationDist > stars[i].dist(stars[j])) {
					starConstellations[i].add(j);
				}
			}
		}

		//Initiate island
		float inversOfVertexNum = 1f / (float) meshVertexNum;
		for(int x = 0; x < meshVertexNum; x ++) {
			for(int z = 0; z < meshVertexNum; z ++) {					
				float xPos = WormApplication.WIDTH / 2 - worldWidth / 2 + worldWidth / meshVertexNum * x;
				
				float xFactor = PApplet.sin(inversOfVertexNum * x * pi);
				float zFactor = PApplet.sin(inversOfVertexNum * z * pi);
				float noiseFactor = p.noise((float) x * islandNoiseScale, (float) z * islandNoiseScale);
				float islandHeight = islandMaxHeight * xFactor * zFactor * noiseFactor;
				float yPos = WormApplication.HEIGHT / 2 + worldHeight / 2 + islandMaxHeight -  islandHeight;
				
				float zPos =  - worldDepth / 2 + worldDepth / meshVertexNum * z;

				islandMesh[x][z] = new PVector(xPos, yPos, zPos);				
				islandMeshColors[x][z] = backgroundColor + (255f - backgroundColor) * islandHeight / islandMaxHeight;
			}	
		}
		
		//Initiate Sea
		float yPos = WormApplication.HEIGHT / 2 + worldHeight / 2 + islandMaxHeight - 100;
		float xPosInner = WormApplication.WIDTH / 2 + innerCircleRadius;
		float zPosInner = innerCircleRadius;
		float xPosOuter = WormApplication.WIDTH / 2 + outerCircleRadius;
		float zPosOuter = outerCircleRadius;
		
		float rotationSteps = doublePi / (float) (seaCircleNum - 1);
		float xDelta;
		float zDelta;		
		for(int i = 0; i < seaCircleNum; i ++) {			
			innerSeaCircle[i] = new PVector();
			innerSeaCircle[i].set(xPosInner, yPos, zPosInner);
			outerSeaCircle[i] = new PVector();
			outerSeaCircle[i].set(xPosOuter, yPos, zPosOuter);
			
			/*Rotate around y axis:
				x' = z*sin q + x*cosines q
			 	z' = z*cosines q - x*sin q
			 */
			//Inner circle
			xDelta = zPosInner * PApplet.sin(rotationSteps) + xPosInner * PApplet.cos(rotationSteps);
			zDelta = zPosInner * PApplet.cos(rotationSteps) - xPosInner * PApplet.sin(rotationSteps);
			xPosInner = xDelta;
			zPosInner = zDelta;
			
			//Outer circle
			xDelta = zPosOuter * PApplet.sin(rotationSteps) + xPosOuter * PApplet.cos(rotationSteps);
			zDelta = zPosOuter * PApplet.cos(rotationSteps) - xPosOuter * PApplet.sin(rotationSteps);
			xPosOuter = xDelta;
			zPosOuter = zDelta;
		}
		
	}

	public void update() {
		// TODO Auto-generated method stub
		
	}

	public void draw() {
		p.background(backgroundColor);
		
		//Draw sea
		int i;
		int iMinusOne;
		for(i = 1; i < seaCircleNum; i ++) {
			iMinusOne = i - 1;
			p.noStroke();
			p.beginShape(PApplet.QUADS);
				p.fill(innerCircleColor);
				p.vertex(innerSeaCircle[i].x, innerSeaCircle[i].y, innerSeaCircle[i].z);
				p.fill(outerCircleColor, 0);
				p.vertex(outerSeaCircle[i].x, outerSeaCircle[i].y, outerSeaCircle[i].z);
				p.vertex(outerSeaCircle[iMinusOne].x, outerSeaCircle[iMinusOne].y, outerSeaCircle[iMinusOne].z);
				p.fill(innerCircleColor);
				p.vertex(innerSeaCircle[iMinusOne].x, innerSeaCircle[iMinusOne].y, innerSeaCircle[iMinusOne].z);
			p.endShape();
			p.beginShape(PApplet.TRIANGLES);
				p.fill(innerCircleColor);
				p.vertex(innerSeaCircle[i].x, innerSeaCircle[i].y, innerSeaCircle[i].z);
				p.vertex(worldCenter.x, outerSeaCircle[i].y, 0);
				p.vertex(innerSeaCircle[iMinusOne].x, innerSeaCircle[iMinusOne].y, innerSeaCircle[iMinusOne].z);
			p.endShape();
		}
		
		//Initiate sky
		for(i = 0; i < starCount; i ++) {
			//Draw stars vertex as point			
			p.beginShape(PApplet.POINTS);
				p.fill(starColors[i]);
				p.stroke(starColors[i]);	
				p.vertex(stars[i].x, stars[i].y, stars[i].z);
			p.endShape();
			
			//Draw constellations
			LinkedList<Integer> starNeighbours = starConstellations[i];
			for(Integer n: starNeighbours) {
				p.noFill();				
				p.beginShape();
					p.stroke(starColors[i], 20);	
					p.vertex(stars[i].x, stars[i].y, stars[i].z);
					p.stroke(starColors[n], 20);
					p.vertex(stars[n].x, stars[n].y, stars[n].z);
				p.endShape();
			}
		}
			
		//Islands
		//Declare variables outside of loop to optimize speed
		int x;
		int xMinusOne;
		int z;
		int zMinusOne;
		for(x = 0; x < meshVertexNum; x ++) {
			xMinusOne = x - 1;
			for(z = 0; z < meshVertexNum; z ++) {
				zMinusOne = z - 1;
				
				//Draw vertex as point
				p.beginShape(PApplet.POINTS);
					p.stroke(255);
					p.fill(255);
					p.vertex(islandMesh[x][z].x, islandMesh[x][z].y, islandMesh[x][z].z);
				p.endShape();
				
				//Draw rectangles
				
				if(x > 0 && z > 0) {
					p.beginShape(PApplet.QUADS);
						p.noStroke();
						//p.stroke(seaColor, 50);
						p.fill(islandMeshColors[x][z]);
						p.vertex(islandMesh[x][z].x, islandMesh[x][z].y, islandMesh[x][z].z);
						p.fill(islandMeshColors[xMinusOne][z]);
						p.vertex(islandMesh[xMinusOne][z].x, islandMesh[xMinusOne][z].y, islandMesh[xMinusOne][z].z);
						p.fill(islandMeshColors[xMinusOne][zMinusOne]);
						p.vertex(islandMesh[xMinusOne][zMinusOne].x, islandMesh[xMinusOne][zMinusOne].y, islandMesh[xMinusOne][zMinusOne].z);
						p.fill(islandMeshColors[x][zMinusOne]);
						p.vertex(islandMesh[x][zMinusOne].x, islandMesh[x][zMinusOne].y, islandMesh[x][zMinusOne].z);
					p.endShape();
				}
			}	
		}
	}

	@Override
	public boolean checkCollisions(PVector[] objectPositions) {
		// TODO Auto-generated method stub
		return false;
	}

	public PVector getSize() {
		return new PVector(worldWidth, worldHeight, worldDepth);
	}

	public PVector getCenter() {
		return worldCenter;
	}

}
